package com.inpion.biz.dataStorage;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Set;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

import com.inpion.biz.repository.handler.IDataStoreHandler;

public class DataStorage {

	private static final String TEMPLET_VALUE_SURFIX = "=%>";
	private static final String TEMPLET_VALUE_PREFIX = "<%=";
	public static final String NOQUOTES = "_#NQ";
	private static SAXBuilder builder = new SAXBuilder();
	private static String staticConfigPath;
	private String configPath;
	private Document configDocument;

	public static void init(String storageConfig) {
		staticConfigPath = storageConfig;
	}
	
	public DataStorage(String configPath) {		
		this.setConfigPath(configPath);
	}

	public static DataStorage newInstance(String configPath) {
		return new DataStorage(configPath);
	}
	
	public static DataResult executeJob(DataServiceJob job) {
		return executeJob(staticConfigPath, job);
	}

	public static DataResult executeJob(String storageConfig, DataServiceJob job) {
		
		DataStorage storage = new DataStorage(storageConfig);

		try {
			return storage.execute(job);
		} catch (ConfigDocumentIsNotFoundException e) {
			e.printStackTrace();
		} catch (QueryIsNotDefinedException e) {
			e.printStackTrace();
		} catch (QueryIdIsNotDefineException e) {
			e.printStackTrace();
		} catch (TargtHandlerClassIsNotDefinedException e) {
			e.printStackTrace();
		} catch (ConnectionIsNotDefinedException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public DataResult execute(DataServiceJob job) throws ConfigDocumentIsNotFoundException, QueryIsNotDefinedException, QueryIdIsNotDefineException, TargtHandlerClassIsNotDefinedException, ConnectionIsNotDefinedException {
		
		DataResult retResult = null;
		
		if (configDocument == null){
			throw new ConfigDocumentIsNotFoundException();
		}
		
		if (job.getQueryId() == null){
			throw new QueryIdIsNotDefineException();
		}
		
		try {
			
			Element connectionElement = (Element) XPath.selectSingleNode(configDocument, "//connection[@id='" + job.getTarget() + "']");
			
			if (connectionElement == null){
				throw new ConnectionIsNotDefinedException();
			}
			
			String handlerClassString = connectionElement.getAttributeValue("class");
			
			if (handlerClassString == null){
				throw new TargtHandlerClassIsNotDefinedException();
			}
			
			Element queryElement = (Element) XPath.selectSingleNode(configDocument, "//query[@id='" + job.getQueryId() + "']");
			
			if (queryElement == null){
				throw new QueryIsNotDefinedException();
			}
			
			IDataStoreHandler idsh = getHandler(handlerClassString);
			
			String targetQuery = queryElement.getValue();
			String queryString = buildQuery(targetQuery, job);
			
			System.out.println("query : " + queryString);
			System.out.println("targetHandler : " + idsh);
			idsh.setQuery(queryString);
			retResult = idsh.execute();
			
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

		return retResult;
		
	}

	private IDataStoreHandler getHandler(String handlerClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		Class<?> hClass = Class.forName(handlerClass);
		Object handlerObject = hClass.newInstance();
		
		return (IDataStoreHandler)handlerObject;
	}

	private String buildQuery(String targetQuery, DataServiceJob job) {
		Parameters dataValue = job.getDataValue();
		String retQuery = targetQuery;
		
		Set<String> keySet = dataValue.keySet();
		for(String keyString : keySet){
			Object objValue = dataValue.get(keyString);
			String valueString = convertObjectString(objValue);
			if (valueString != null){
				System.out.println("objValue : " + valueString);
				retQuery = retQuery.replaceAll(TEMPLET_VALUE_PREFIX + keyString +TEMPLET_VALUE_SURFIX, valueString) ;
			}
		}
		
		return retQuery;
	}

	private String convertObjectString(Object objValue) {
		String retType_01 = objValue.toString();
		String retType_02 = "'" + objValue + "'";
		
		if (objValue instanceof Number){
			return retType_01;
		}else if (objValue instanceof String){
			if (((String) objValue).endsWith(NOQUOTES)){
				return retType_01.replaceAll(NOQUOTES, "");
			}else{
				return retType_02;
			}
		}else if (objValue instanceof Date){
			return retType_02;
		}else if (objValue instanceof Timestamp){
			return retType_02;
		}
		
		return null;
	}

	public String getConfigPath() {
		return configPath;
	}

	public void setConfigPath(String configPath) {
		this.configPath = configPath;
		
		try {
			configDocument = builder.build(this.configPath);
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	public static String getQuery(String configPath, String queryId) throws ConfigDocumentIsNotFoundException, ConfigDocumentProcessException {


		Document rootDocument = null;
		
		if (configPath == null){
			throw new ConfigDocumentIsNotFoundException();
		}else{
			
			try {
				rootDocument = builder.build(configPath);
			} catch (JDOMException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
						
		try {
			Element connectionElement = (Element) XPath.selectSingleNode(rootDocument, "//collection[@id='queryCollection']/query[@id='"+queryId+"']");
			if (connectionElement == null) return null;			
			return connectionElement.getValue();
		} catch (JDOMException e) {
			e.printStackTrace();
			throw new ConfigDocumentProcessException();			
		}
	}

}
